package com.yejiarong.auth.service.impl;

import com.yejiarong.auth.bean.dto.OAuth2ClientSaveDto;
import com.yejiarong.auth.service.IClientRegistrationService;
import com.yejiarong.common.validate.ValidateUtils;
import jakarta.annotation.PostConstruct;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;
import java.util.UUID;

/**
 * @author YeJR
 */
@Service
public class ClientRegistrationServiceImpl implements IClientRegistrationService {

    private final RegisteredClientRepository registeredClientRepository;

    private final PasswordEncoder passwordEncoder;

    public ClientRegistrationServiceImpl(RegisteredClientRepository registeredClientRepository, PasswordEncoder passwordEncoder) {
        this.registeredClientRepository = registeredClientRepository;
        this.passwordEncoder = passwordEncoder;
    }

    @PostConstruct
    public void init() {
        String clientId = "gateway-client";
        OAuth2ClientSaveDto oAuth2ClientSaveDto = new OAuth2ClientSaveDto();
        oAuth2ClientSaveDto.setClientId(clientId);
        oAuth2ClientSaveDto.setClientName("Gateway Client");
        oAuth2ClientSaveDto.setClientAuthenticationMethods("client_secret_basic");
        oAuth2ClientSaveDto.setAuthorizationGrantTypes("authorization_code,refresh_token,client_credentials");
        oAuth2ClientSaveDto.setRedirectUri("http://localhost:8080");
        oAuth2ClientSaveDto.setLogoutRedirectUri("http://localhost:8080");
        oAuth2ClientSaveDto.setScopes(Arrays.asList("openid","profile","data.read","data.write"));
//        saveOAuth2Client(oAuth2ClientSaveDto);


        Base64.Encoder encoder = Base64.getEncoder();
        String secret = encoder.encodeToString(clientId.getBytes());
        System.out.println(secret);


        System.out.println(encoder.encodeToString((clientId + ":" + secret).getBytes()));
    }

    /**
     * 保存oauth2 client
     * @param dto dto
     * @return client_secret
     */
    @Override
    public String saveOAuth2Client(OAuth2ClientSaveDto dto) {
        ValidateUtils.valid(dto);

        RegisteredClient repeatClient = registeredClientRepository.findByClientId(dto.getClientId());
        ValidateUtils.isNull(repeatClient, "ClientId重复");

        Base64.Encoder encoder = Base64.getEncoder();
        String clientSecret = encoder.encodeToString(dto.getClientId().getBytes());

        RegisteredClient.Builder builder = RegisteredClient
                .withId(UUID.randomUUID().toString())
                .clientId(dto.getClientId())
                .clientName(dto.getClientName())
                .clientSecret(passwordEncoder.encode(clientSecret))
                .redirectUri(dto.getRedirectUri())
                .postLogoutRedirectUri(dto.getRedirectUri());

        Arrays.stream(dto.getClientAuthenticationMethods().split(",")).distinct()
                .forEach(s -> builder.clientAuthenticationMethod(new ClientAuthenticationMethod(s)));
        Arrays.stream(dto.getAuthorizationGrantTypes().split(",")).distinct()
                .forEach(s -> builder.authorizationGrantType(new AuthorizationGrantType(s)));
        dto.getScopes().forEach(builder::scope);

        ClientSettings.Builder clientSettingsBuilder = ClientSettings.builder().requireAuthorizationConsent(dto.isRequireAuthorizationConsent());
        if (dto.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.NONE.getValue())) {
            clientSettingsBuilder.requireProofKey(true);
        }
        builder.clientSettings(clientSettingsBuilder.build());


        TokenSettings.Builder tokenSettingsBuild = TokenSettings.builder()
                // AccessToken 过期时间
                .accessTokenTimeToLive(Objects.requireNonNull(getDurationTime(dto.getAccessTokenLiveTime(), dto.getAccessTokenLiveTimeUnit())))
                // RefreshAccessToken 过期时间
                .refreshTokenTimeToLive(Objects.requireNonNull(getDurationTime(dto.getRefreshAccessTokenLiveTime(), dto.getRefreshAccessTokenLiveTimeUnit())))
                // OIDC 客户端注册 需要指定 ID Token签名算法
                .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256);
        builder.tokenSettings(tokenSettingsBuild.build());

        // 保存
        registeredClientRepository.save(builder.build());
        return clientSecret;
    }

    /**
     * 获取Duration
     * @param time 时间
     * @param timeUnit 单位
     * @return Duration
     */
    private Duration getDurationTime(Long time, String timeUnit) {
        return switch (timeUnit) {
            case "MINUTES" -> Duration.ofMinutes(time);
            case "HOURS" -> Duration.ofHours(time);
            case "DAYS" -> Duration.ofDays(time);
            default -> null;
        };
    }
}
